package com.zwcl.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zwcl.common.core.constant.CacheConstants;
import com.zwcl.common.core.constant.Constants;
import com.zwcl.common.core.domain.entity.ApiResult;
import com.zwcl.common.core.enums.ExceptionCode;
import com.zwcl.common.core.exception.SystemEmergentException;
import com.zwcl.common.core.redis.RedisService;
import com.zwcl.common.core.utils.StringUtilsEx;
import com.zwcl.common.core.utils.text.UUID;
import com.zwcl.gateway.properties.EmergentNoticeProperties;
import com.zwcl.gateway.properties.IgnoreWhiteProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * 网关鉴权
 * 
 * @author xyp
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;

    // 排除过滤的 uri 地址，nacos自行添加
    @Autowired
    private IgnoreWhiteProperties ignoreWhiteProperties;

    @Autowired
    private EmergentNoticeProperties emergentNoticeProperties;

    @Resource(name = "stringRedisTemplate")
    private ValueOperations<String, String> sops;
    
    @Autowired
    private RedisService redisService;

//  测试配置是否正常
//    @Value("${emergent.emergentFlag}")
//    private Boolean emergentFlag;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();
        log.info("请求地址："+url);
        if(emergentNoticeProperties.getEmergentFlag()){
            throw new SystemEmergentException(ExceptionCode.SYSTEM_EMERGENT_NOTICE.getCode(),String.format("%s，请稍等%s分钟再试",emergentNoticeProperties.getMsg(),emergentNoticeProperties.getMinute()));
        }
        String traceId = UUID.randomUUID().toString();
        log.info("请求链路ID:"+traceId);

        // 跳过不需要验证的路径
        if (StringUtilsEx.matches(url, ignoreWhiteProperties.getWhiteUrls())){
            ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(CacheConstants.REQUEST_TRACE, traceId).build();
            ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
            return chain.filter(mutableExchange);
        }
        String token = getToken(exchange.getRequest());
        if (StringUtils.isBlank(token)){
            return setUnauthorizedResponse(exchange, ExceptionCode.NO_HEAD_TOKEN.getCode(),"令牌不能为空");
        }
        log.info("登录的token 的key是：{}",getTokenKey(token));
        // 从缓存中取login_tokens:token的数据
        String userStr = sops.get(getTokenKey(token));
        if (StringUtils.isBlank(userStr)){
            return setUnauthorizedResponse(exchange,ExceptionCode.TOKEN_ISNOT_VALID.getCode(), "登录状态已过期");
        }
        //注意：网关最好不要依赖其他库，否则容易引发包冲突，因此采用JsonObject解析。
        JSONObject obj = JSONObject.parseObject(userStr);
        String from = obj.getString("from");
        String appCode = obj.getString("appCode");
        String userid = obj.getInteger("userId").toString();
        if (null==userid || StringUtils.isBlank(from) || StringUtils.isBlank(appCode)){
            return setUnauthorizedResponse(exchange, ExceptionCode.AUTHENTICATION_EXCEPTION.getCode(), "令牌验证失败");
        }
        // 设置过期时间
        redisService.expire(getTokenKey(token), EXPIRE_TIME);

        //设置用户信息到请求，注意，这里是追加头部信息，token信息已经有了
        ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(CacheConstants.DETAILS_USER_ID, userid)
                .header(CacheConstants.DETAILS_CLIENIT, from).header(CacheConstants.DETAILS_FROM_APP,appCode)
                .header(CacheConstants.REQUEST_TRACE,traceId).build();

        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
        return chain.filter(mutableExchange);
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange,Integer errCode, String msg){
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        //输出错误
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(ApiResult.fail(errCode,msg)));
        }));
    }

    private String getTokenKey(String token){
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }

    /**
     * 获取请求token
     */
    private String getToken(ServerHttpRequest request){
        String token = request.getHeaders().getFirst(CacheConstants.HEADER);
        if (StringUtilsEx.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)){
            token = token.replace(CacheConstants.TOKEN_PREFIX, "");
        }
        return token;
    }

    @Override
    public int getOrder()
    {
        return -200;
    }
}